#ifndef __CC_IP_H__
#define __CC_IP_H__

#include <stdint.h>
#include <stdlib.h>

struct iphdr
{
    /*++ IP Header Format
        0                                 1                                 2                                 3
        0  1   2  3  4  5   6  7   0  1   2  3  4   5  6   7  0  1   2  3  4  5   6   7  0  1   2  3  4   5  6   7
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |    Version |        IHL   |          Type of Service|                                            Total Length      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                         Identification         |    Flags|                         Fragment Offset     |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Time to Live             |                Protocol    |                                      Header Checksum   |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                        Source Address                                                                                            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                                                       Destination Address                       |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                     Options                                                                        |                    Padding |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     IP Header FormatRFC-791
     --*/
#ifdef __CC_BIG_ENDIAN
    uint8_t version: 4;
    uint8_t hdrlen: 4;
    uint8_t tos;
    uint16_t tot_len;
    uint16_t id;
    uint16_t flag: 3;
    uint16_t frag_offset: 13;
    uint8_t ttl;
    uint8_t protocol;
    uint16_t checksum;
    uint32_t saddr;
    uint32_t daddr;
#else
    uint8_t hdrlen: 4;
    uint8_t version: 4;
    uint8_t tos;
    uint16_t tot_len;
    uint16_t id;
    uint16_t frag_offset: 13;
    uint16_t flag: 3;
    uint8_t ttl;
    uint8_t protocol;
    uint16_t checksum;
    uint32_t saddr;
    uint32_t daddr;
#endif
} __attribute__((__packed__));

#define IPHDR_LEN 20

/**
 * Set ipv4 address.
 *
 * eg. set x address "255.255.255.255", you need just invoke IP_ADDR(x, 255, 255, 255, 255).
 */
#define IPv4_ADDR_SET(addr, a, b, c, d) ((addr) = htonl( ((uint32_t)((a) & 0xff) << 24) | \
    ((uint32_t)((b) & 0xff) << 16) | \
    ((uint32_t)((c) & 0xff) << 8) |\
    ((uint32_t)((d) & 0xff))\
    ))

#define NIPQUAD(addr) \
    ((unsigned char *)&(addr))[0], \
    ((unsigned char *)&(addr))[1], \
    ((unsigned char *)&(addr))[2], \
    ((unsigned char *)&(addr))[3]

uint32_t subnet_id(uint32_t addr, uint32_t mask);
uint32_t subnet_broadcast(uint32_t addr, uint32_t mask);
int subnet_range(uint32_t addr, uint32_t mask, uint32_t* start, uint32_t* end);

uint16_t ipv4hdr_chksum(uint16_t *w, int blen);

struct subnet_info
{
    uint32_t id;  // subnet id
    uint32_t start;
    uint32_t end;
    uint32_t netmask;

    uint32_t start_h;  //  in host byte order
    uint32_t end_h;  // int host byte order
    uint32_t netmask_h;
    int host_count;
};


int subnet_info_init(struct subnet_info* p, uint32_t addr, int mask_bits);

#endif

